package edu.towson.cosc.cosc455.dspenc5.compiler;

import edu.towson.cosc.cosc455.interfaces.SyntaxAnalyzer;

public class MySyntaxAnalyzer implements SyntaxAnalyzer {

	@Override
	public void lolcode() {
		// #HAI <comment> <variable-define> <head> <body> #KTHXBYE
		if (!MyCompiler.token.equalsIgnoreCase(LexicalTokens.DOC_BEGIN)) {
			System.err.println("SYNTAX ERROR: " + MyCompiler.token
					+ " found, when " + LexicalTokens.DOC_BEGIN
					+ " was expected.");
			System.exit(0);
		}

		MyCompiler.parseTree.add(MyCompiler.token);
		MyCompiler.lexer.getNextToken();

		if (!MyCompiler.token.startsWith("#")) {
			text();
		}
		if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.COMMENT_BEGIN)) {
			comment();
		}

		if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.VARIABLE_BEGIN)) {
			define();
		}

		if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.HEAD_BEGIN)) {
			head();
		}
		
			body();

		if (!MyCompiler.token.equalsIgnoreCase(LexicalTokens.DOC_END)) {
			System.err.println("SYNTAX ERROR: " + MyCompiler.token
					+ " found, when " + LexicalTokens.DOC_END
					+ " was expected.");
			System.exit(0);
		}
		MyCompiler.parseTree.add(MyCompiler.token);
		/*
		 * The loop below checks if tokens exist after the #kthxbye token and errors out if there 
		 * are indeed tokens after it.
		 */
		while(MyCompiler.lexer.getCurrentPosition() < MyCompiler.lexer.getCompleteCode().length()){
			MyCompiler.lexer.getNextToken();
			if(MyCompiler.token.contains("#")){
				System.err.println("SYNTAX ERROR: The following token was found after " + LexicalTokens.DOC_END 
						+ ": " + MyCompiler.token +
						"\nTokens cannot appear after the " +LexicalTokens.DOC_END +" tag.");
				System.exit(0);
			}
		}
	}

	@Override
	public void comment() {
		MyCompiler.parseTree.add(MyCompiler.token);
		MyCompiler.lexer.getNextToken();
		if (!MyCompiler.token.startsWith("#")) {// continually processes text
			// until tag token is hit
			text();
		}
		if (!MyCompiler.token.equalsIgnoreCase(LexicalTokens.COMMENT_END)) {
			System.err.println("SYNTAX ERROR: " + MyCompiler.token
					+ " found, when " + LexicalTokens.COMMENT_END
					+ " was expected.");
			System.exit(0);
		}
		MyCompiler.parseTree.add(MyCompiler.token);
		MyCompiler.lexer.getNextToken();
	}

	@Override
	public void head() {
		MyCompiler.parseTree.add(MyCompiler.token);
		MyCompiler.lexer.getNextToken();

		if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.TITLE_BEGIN)) {
			title();
		}

		if (!MyCompiler.token.equalsIgnoreCase(LexicalTokens.HEAD_END)) {
			System.err.println("SYNTAX ERROR: " + MyCompiler.token
					+ " found, when " + LexicalTokens.HEAD_END
					+ " was expected.");
			System.exit(0);
		}

		MyCompiler.parseTree.add(MySemanticAnalyzer.HEAD_END);
		MyCompiler.lexer.getNextToken();

	}

	@Override
	public void title() {
		MyCompiler.parseTree.add(MyCompiler.token);
		MyCompiler.lexer.getNextToken();
		while (!MyCompiler.token.startsWith("#")) {
			text();
		}
		if (!MyCompiler.token.equalsIgnoreCase(LexicalTokens.TITLE_END)) {
			System.err.println("SYNTAX ERROR: " + MyCompiler.token
					+ " found, when " + LexicalTokens.TITLE_END
					+ " was expected.");
			System.exit(0);
		}
		MyCompiler.parseTree.add(MySemanticAnalyzer.TITLE_END);
		MyCompiler.lexer.getNextToken();

	}

	@Override
	public void paragraph() {
		MyCompiler.parseTree.add(MyCompiler.token);
		MyCompiler.lexer.getNextToken();
		
		if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.VARIABLE_BEGIN)) {
			define();
		}

		inner_paragraph();

		if (!MyCompiler.token.equalsIgnoreCase(LexicalTokens.PARAGRAPH_END)) {
			System.err.println("SYNTAX ERROR: " + MyCompiler.token
					+ " found, when " + LexicalTokens.PARAGRAPH_END
					+ " was expected.");
			System.exit(0);
		}
		MyCompiler.parseTree.add(MySemanticAnalyzer.PARAGRAPH_END);
		MyCompiler.lexer.getNextToken();
	}

	@Override
	public void bold() {
		MyCompiler.parseTree.add(MyCompiler.token);
		MyCompiler.lexer.getNextToken();
		if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.VARIABLE_BEGIN)) {
			define();
		}
		while (!MyCompiler.token.startsWith("#")) {
			text();
		}
		if (!MyCompiler.token.equalsIgnoreCase(LexicalTokens.BOLD_END)) {
			System.err.println("SYNTAX ERROR: " + MyCompiler.token
					+ " found, when " + LexicalTokens.BOLD_END
					+ " was expected.");
			System.exit(0);
		}
		MyCompiler.parseTree.add(MySemanticAnalyzer.BOLD_END);
		MyCompiler.lexer.getNextToken();
	}

	@Override
	public void italics() {
		MyCompiler.parseTree.add(MyCompiler.token);
		MyCompiler.lexer.getNextToken();
		if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.VARIABLE_BEGIN)) {
			define();
		}
		while (!MyCompiler.token.startsWith("#")) {
			text();
		}
		if (!MyCompiler.token.equalsIgnoreCase(LexicalTokens.ITALICS_END)) {
			System.err.println("SYNTAX ERROR: " + MyCompiler.token
					+ " found, when " + LexicalTokens.ITALICS_END
					+ " was expected.");
			System.exit(0);
		}
		MyCompiler.parseTree.add(MySemanticAnalyzer.ITALICS_END);
		MyCompiler.lexer.getNextToken();
	}

	@Override
	public void list() {
		MyCompiler.parseTree.add(MyCompiler.token);
		MyCompiler.lexer.getNextToken();

		if (!MyCompiler.token.equalsIgnoreCase(LexicalTokens.ITEM_START)) {
			System.err.println("SYNTAX ERROR: " + MyCompiler.token
					+ " found, when " + LexicalTokens.ITEM_START
					+ " was expected.");
			System.exit(0);
		}
		item();
		if (!MyCompiler.token.equalsIgnoreCase(LexicalTokens.LIST_END)) {
			System.err.println("SYNTAX ERROR: " + MyCompiler.token
					+ " found, when " + LexicalTokens.LIST_END
					+ " was expected.");
			System.exit(0);
		}
		MyCompiler.parseTree.add(MySemanticAnalyzer.LIST_END);
		MyCompiler.lexer.getNextToken();
	}

	@Override
	public void item() {
		MyCompiler.parseTree.add(MyCompiler.token);
		MyCompiler.lexer.getNextToken();
		inner_list();
		if (!MyCompiler.token.equalsIgnoreCase(LexicalTokens.ITEM_END)) {
			System.err.println("SYNTAX ERROR: " + MyCompiler.token
					+ " found, when " + LexicalTokens.ITEM_END
					+ " was expected.");
			System.exit(0);
		}
		MyCompiler.parseTree.add(MySemanticAnalyzer.ITEM_END);
		MyCompiler.lexer.getNextToken();
		if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.ITEM_START)) {
			item();
		}
		
	}

	@Override
	public void audio() {
		MyCompiler.parseTree.add(MyCompiler.token);
		MyCompiler.lexer.getNextToken();
		while (!MyCompiler.token.startsWith("#")) {
			text();
		}
		if (!MyCompiler.token.equalsIgnoreCase(LexicalTokens.AUDIO_END)) {
			System.err.println("SYNTAX ERROR: " + MyCompiler.token
					+ " found, when " + LexicalTokens.AUDIO_END
					+ " was expected.");
			System.exit(0);
		}
		MyCompiler.parseTree.add(MySemanticAnalyzer.AUDIO_END);
		MyCompiler.lexer.getNextToken();
	}

	@Override
	public void video() {
		MyCompiler.parseTree.add(MyCompiler.token);
		MyCompiler.lexer.getNextToken();
		while (!MyCompiler.token.startsWith("#")) {
			text();
		}
		if (!MyCompiler.token.equalsIgnoreCase(LexicalTokens.VIDEO_END)) {
			System.err.println("SYNTAX ERROR: " + MyCompiler.token
					+ " found, when " + LexicalTokens.VIDEO_END
					+ " was expected.");
			System.exit(0);
		}
		MyCompiler.parseTree.add(MySemanticAnalyzer.VIDEO_END);
		MyCompiler.lexer.getNextToken();
	}

	@Override
	public void define() {
		MyCompiler.parseTree.add(MyCompiler.token);
		MyCompiler.lexer.getNextToken();

		if (MyCompiler.token.startsWith("#")) {
			System.err.println("SYNTAX ERROR: " + MyCompiler.token
					+ " found, when text was expected."
					+ " When defining a variable, it needs a name.");
			System.exit(0);
		}
		String varName = "";
		String varDefinition = "";
		while (!MyCompiler.token.startsWith("#")) {
			varName = varName + MyCompiler.token;
			MyCompiler.lexer.getNextToken();
		}
		//chops off the trailing white space then checks if string contains space in middle of varname
			if(varName.trim().contains(" ")){
			System.err.println("SYNTAX ERROR: Variable name: " + varName + " contains a space, which is invalid");
			System.exit(0);
		}
		if (!MyCompiler.token.equalsIgnoreCase(LexicalTokens.VARIABLE_MIDDLE)) {
			System.err.println("SYNTAX ERROR: Found " + MyCompiler.token
					+ " when " + LexicalTokens.VARIABLE_MIDDLE
					+ " was expected");
			System.exit(0);
		}
		else{
			MyCompiler.parseTree.add(MyCompiler.token);
			MyCompiler.lexer.getNextToken();
		}

		if (MyCompiler.token.startsWith("#")) {
			System.err.println("SYNTAX ERROR: " + MyCompiler.token
					+ " found, when text was expected."
					+ " When defining a variable, it needs a name.");
			System.exit(0);
		}

		while (!MyCompiler.token.startsWith("#")) {
			varDefinition = varDefinition + MyCompiler.token;
			MyCompiler.lexer.getNextToken();
		}

		if (!MyCompiler.token.equalsIgnoreCase(LexicalTokens.VARIABLE_END)) {
			System.err.println("SYNTAX ERROR: Found " + MyCompiler.token
					+ " when " + LexicalTokens.VARIABLE_END + " was expected");
			System.exit(0);
		}
		if(!MySemanticAnalyzer.conversionMap.containsKey(varName.toUpperCase())){
		MySemanticAnalyzer.conversionMap.put(varName.toUpperCase(), varDefinition);
		}
		else{
			MySemanticAnalyzer.conversionMap.put(varName.toUpperCase()+varName.toUpperCase(), varDefinition);
		}
		MyCompiler.parseTree.add(MyCompiler.token);
		MyCompiler.lexer.getNextToken();
		
		if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.VARIABLE_BEGIN)) {
			define();
		}
	}

	@Override
	public void use() {
		MyCompiler.parseTree.add(MyCompiler.token);
		MyCompiler.lexer.getNextToken();

		if (MyCompiler.token.startsWith("#")) {
			System.err.println("SYNTAX ERROR: " + MyCompiler.token
					+ " found, when text was expected."
					+ " When call a variable, its name must be text.");
			System.exit(0);
		}

		while (!MyCompiler.token.startsWith("#")) {
			text();
		}
		
		if (!MyCompiler.token.equalsIgnoreCase(LexicalTokens.VARIABLE_USE_END)) {
			System.err.println("SYNTAX ERROR: Found " + MyCompiler.token
					+ " when " + LexicalTokens.VARIABLE_USE_END + " was expected");
			System.exit(0);
		}
		MyCompiler.parseTree.add(MyCompiler.token);
		MyCompiler.lexer.getNextToken();

	}
	//adds text to parseTree array list then gets the next token
	public void text() {
		MyCompiler.parseTree.add(MyCompiler.token);
		MyCompiler.lexer.getNextToken();
	}

	@Override
	public void body() {
		inner_text();
		if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.PARAGRAPH_BEGIN)) {
			paragraph();
			body();
		}
		if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.BOLD_BEGIN)) {
			bold();
			body();
		}
		if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.ITALICS_BEGIN)) {
			italics();
			body();
		}
		if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.LIST_BEGIN)) {
			list();
			body();
		}
	}
	/**
	 * This method implements the BNF grammar rule for inner_list
	 */
	private void inner_list() {
		inner_text();
		if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.BOLD_BEGIN)) {
			bold();
			inner_list();
		}
		if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.ITALICS_BEGIN)) {
			italics();
			inner_list();
		}
		if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.LIST_BEGIN)) {
			list();
			inner_list();
		}
	}
	/**
	 * This method implements the BNF grammar rule for inner_paragraph
	 */
	private void inner_paragraph() {
		inner_text();
		if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.BOLD_BEGIN)) {
			bold();
			inner_paragraph();
		}
		if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.ITALICS_BEGIN)) {
			italics();
			inner_paragraph();
		}
		if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.LIST_BEGIN)) {
			list();
			inner_paragraph();
		}
		if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.NEWLINE)) {
			newline();
			inner_paragraph();
		}
	}
	/**
	 * This method implements the BNF grammar rule for inner_text
	 */
	private void inner_text() {
		if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.VARIABLE_USE_BEGIN)) {
			use();
			inner_text();
		}
		if (!MyCompiler.token.startsWith("#")) {
			while (!MyCompiler.token.startsWith("#")) {
				text();
			}
			inner_text();
		}
		if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.NEWLINE)) {
			newline();
			inner_text();
		}
		if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.AUDIO_BEGIN)) {
			audio();
			inner_text();
		}
		if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.VIDEO_BEGIN)) {
			video();
			inner_text();
		}
	}
	/**
	 * This method implements the BNF grammar rule for the newline annotation.
	 */
	private void newline() {
		MyCompiler.parseTree.add(MyCompiler.token);
		MyCompiler.lexer.getNextToken();
		if (MyCompiler.token.equalsIgnoreCase(LexicalTokens.NEWLINE)) {
			newline();
		}
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub

	}

}
